using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using CRM.Models.Email;
using CRM.ViewModels.Email;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using RushMail;
using RushMail.Requests;

namespace CRM.Services.Email;

public class RushMailApplicationService : BaseService
{
    private IRushMailClient _rushMailClient;
    private readonly IOemEmailApplicationFactory _applicationFactory;
    private readonly ILogger<RushMailApplicationService> _logger;
    private readonly MyDbContext _dbContext;

    public RushMailApplicationService(IOemEmailApplicationFactory applicationFactory, ILogger<RushMailApplicationService> logger, MyDbContext dbContext)
    {
        _applicationFactory = applicationFactory;
        _logger = logger;
        _dbContext = dbContext;
        
    }

    public async Task RunAsync(EmailMarketing emailMarketing)
    {
        _dbContext.Entry(emailMarketing).State = EntityState.Unchanged;
        var link = (await _dbContext.RushMailLinks.FirstOrDefaultAsync(r => r.EmailMarketingId == emailMarketing.Id))
                   ?? _dbContext.RushMailLinks.Add(new RushMailLink() {EmailMarketingId = emailMarketing.Id}).Entity;
        _rushMailClient = await _applicationFactory.CreateRushMailClient(emailMarketing.OemId);
        // TODO: 清除一定时间以上的无效数据，地址池，模板
        // 创建地址池
        var poolId = await CreateAddressPool(emailMarketing, link, emailMarketing.AddresseeObj);
        // 创建模板
        // TODO: 如有重复模板关联rushmail模板id
        var templateId = await CreateEmailTemplate(emailMarketing, link);

        if (poolId != 0 && templateId != 0)
        {
            // 发送邮件
            var sendFlag = await SendEmail(emailMarketing, link, poolId, templateId);
            if (sendFlag)
            {
                emailMarketing.State = EmailMarketing.StateEnum.Completed;
                link.State = RushMailState.Send;
                _dbContext.AddRange(emailMarketing.AddresseeObj
                    .Select(m => EmailSendHistoryVM.NewRushmailModel(emailMarketing, m)));
            }
        }

        await _dbContext.SaveChangesAsync();
    }

    /// <summary>
    /// 跟踪发送数量
    /// </summary>
    /// <param name="link"></param>
    public async Task TrackSendNumAsync(RushMailLink link)
    {
        if (link is not { TaskId: > 0})
            return;
        
        _dbContext.Entry(link).State = EntityState.Unchanged;
        var emailMarketing =
            await _dbContext.EmailMarketing.Where(m => m.Id == link.EmailMarketingId).FirstOrDefaultAsync();
        if (emailMarketing is null)
        {
            _logger.LogError("无法找到rushmail所关联的营销id#{EmailMarketingId}", link.EmailMarketingId);
            return;
        }
        _rushMailClient = await _applicationFactory.CreateRushMailClient(emailMarketing.OemId);
        var response = await _rushMailClient.Task.View(link.TaskId);
        if (response.IsOk)
        {
            emailMarketing.SendingNum = response.Result.Task.CountSend + response.Result.Task.CountError;

            if (response.Result.Task.CountError > 0)
            {
                // TODO 获取详细失败列表
            }
            
            // 所有邮件发送完成
            if (emailMarketing.SendingNum >= emailMarketing.AddresseeObj.Count)
                link.State = RushMailState.NeedTrack;
        }

        await _dbContext.SaveChangesAsync();
    }

    /// <summary>
    /// 跟踪已读数量
    /// </summary>
    /// <param name="link"></param>
    public async Task TrackReadNum(RushMailLink link)
    {
        if (link is not { TaskId: > 0})
            return;
        
        _dbContext.Entry(link).State = EntityState.Unchanged;
        var emailMarketing =
            await _dbContext.EmailMarketing.Where(m => m.Id == link.EmailMarketingId).FirstOrDefaultAsync();
        if (emailMarketing is null)
        {
            _logger.LogError("无法找到rushmail所关联的营销id#{EmailMarketingId}", link.EmailMarketingId);
            return;
        }
        _rushMailClient = await _applicationFactory.CreateRushMailClient(emailMarketing.OemId);
        var histories = await _dbContext.EmailSendHistory.Where(m => m.Source == emailMarketing.Id)
            .Select(m => new EmailSendHistory()
            {
                Id = m.Id,
                Details = m.Details,
                ReceiverEmail = m.ReceiverEmail
            }).ToDictionaryAsync(x => x.ReceiverEmail, x => x);

        if (link.TrackId <= 0)
        {
            var response = await _rushMailClient.Track.Overview(link.TaskId);
            if (response.IsOk)
                link.TrackId = response.Result.TrackId;
        }

        if (link.TrackId > 0)
        {
            var trackId = link.TrackId;
            var detailResponse = await _rushMailClient.Track.Detail(trackId);
            if (detailResponse.IsOk)
            {
                if (detailResponse.Result is { Count: > 0 } tracks)
                {
                    var addresseeList = emailMarketing.AddresseeObj;
                    var tracksDic = tracks.ToDictionary(x => x.Address, x => x);
                    foreach (var addressee in addresseeList.Where(m => !m.IsRead))
                    {
                        if (tracksDic.TryGetValue(addressee.Addressee, out var item))
                        {
                            addressee.IsRead = item.OpenCount > 0;
                        }

                        if (histories.TryGetValue(addressee.Addressee, out var history))
                        {
                            history.Details |= (long)DetailsEnum.Open;
                        }
                        else
                        {
                            history = EmailSendHistoryVM.NewRushmailModel(emailMarketing, addressee);
                            history.Details |= (long)DetailsEnum.Open;
                            _dbContext.Add(history);
                        }
                    }

                    emailMarketing.Addressee = JsonConvert.SerializeObject(addresseeList);
                    emailMarketing.OpenNum = addresseeList.Count(m => m.IsRead);
                    // 当所有发送成功的邮件已读时停止跟踪
                    if (addresseeList.All(m => m.IsRead || m.IsFailed))
                        link.State = RushMailState.Completed;
                }
            }
        }

        link.LatestTrackTime = DateTime.Now;
        await _dbContext.SaveChangesAsync();
    }

    private async Task<bool> SendEmail(EmailMarketing emailMarketing, RushMailLink link, int poolId, int templateId)
    {
        if (link.TaskId != 0)
            return true;
        
        var taskResponse = await _rushMailClient.Task.Add(new TaskAdd()
        {
            PoolId = poolId,
            TemplateId = templateId,
            TaskName = emailMarketing.Title,
            ReplyTo = emailMarketing.ReplyEmail,
            CountPreset = 0,
            Status = -9,
            Track = 2,
        });
        
        if (!taskResponse.IsOk)
        {
            LogRequestError("邮件发送任务创建", emailMarketing.Id,taskResponse.Content.ToString(Formatting.None));
            return false;
        }

        var taskId = taskResponse.Result;
        link.TaskId = taskId;
        _logger.LogInformation("营销任务#{Id}发送任务创建成功", link.EmailMarketingId);
        // 后续用于跟踪统计
        return true;
    }

    /// <summary>
    /// 创建地址池
    /// </summary>
    /// <param name="emailMarketing"></param>
    /// <param name="link"></param>
    /// <param name="emailMarketingAddresseeObj"></param>
    /// <returns></returns>
    private async Task<int> CreateAddressPool(EmailMarketing emailMarketing, RushMailLink link,
        List<AddresseeObj> emailMarketingAddresseeObj)
    {
        if (link.PoolId != 0)
            return link.PoolId;
        
        var now = DateTime.Now;
        var nowStr = now.ToString("yyyyMMddHHmmss");
        var poolResponse = await _rushMailClient.Pool.Add(new PoolAdd()
        {
            Name = emailMarketing.Title + nowStr,
            PublicName = emailMarketing.Title + nowStr,
        });

        if (!poolResponse.IsOk)
        {
            LogRequestError("地址池创建", emailMarketing.Id,poolResponse.Content.ToString(Formatting.None));
            return 0;
        }
        var poolId = poolResponse.Result;
        link.PoolId = poolId;
        var poolAddressResponse = await _rushMailClient.PoolAddress.Add(new PoolAddressAdd()
        {
            PoolId = poolId,
            Data = emailMarketingAddresseeObj.Select(e => new PoolAddressItem()
            {
                Address = e.Addressee,
                Name = e.Name,
            }).ToList()
        });

        if (!poolAddressResponse.IsOk)
        {
            LogRequestError("地址池添加邮箱", emailMarketing.Id,poolAddressResponse.Content.ToString(Formatting.None));
            return 0;
        }
        
        _logger.LogInformation("营销任务#{Id}地址池创建成功", link.EmailMarketingId);
        return poolId;
    }

    /// <summary>
    /// 创建邮件模板
    /// </summary>
    /// <param name="emailMarketing"></param>
    /// <param name="link"></param>
    /// <returns></returns>
    private async Task<int> CreateEmailTemplate(EmailMarketing emailMarketing, RushMailLink link)
    {
        if (link.TemplateId != 0)
            return link.TemplateId;
        
        var now = DateTime.Now;
        var nowStr = now.ToString("yyyyMMddHHmmss");
        var templateResponse = await _rushMailClient.Template.Add(new TemplateAdd()
        {
            Name = emailMarketing.Title + nowStr,
            Mode = "editor",
            Subject = emailMarketing.EmailSubject,
            TplContent = emailMarketing.EmailContent
        });
        if (!templateResponse.IsOk)
        {
            LogRequestError("模板创建", emailMarketing.Id,templateResponse.Content.ToString(Formatting.None));
            return 0;
        }
        var templateId = templateResponse.Result;
        link.TemplateId = templateId;
        _logger.LogInformation("营销任务#{Id}邮件模板创建成功", link.EmailMarketingId);
        return templateId;
    }

    private void LogRequestError(string requestName, int emailMarketId, string response)
    {
        _logger.LogError("rushmail{RequestName}出错#{EmailMarketId}: {Response}", requestName, emailMarketId, response);
    }

}

public interface IOemEmailApplicationFactory
{
    public Task<IRushMailClient> CreateRushMailClient(int oemId);
}

public class OemEmailApplicationFactory : IOemEmailApplicationFactory
{
    private readonly IServiceProvider _sp;
    private readonly MyDbContext _dbContext;

    public OemEmailApplicationFactory(IServiceProvider sp)
    {
        _sp = sp;
        _dbContext = sp.CreateScope().ServiceProvider.GetRequiredService<MyDbContext>();
    }
    public async Task<IRushMailClient> CreateRushMailClient(int oemId)
    {
        var settings = await _dbContext.Oem.AsNoTracking().Where(o => o.Id == oemId).Select(o => o.EmailSettings).FirstAsync();
        if (settings.EmailPlatform != EmailPlatform.Rushmail)
            throw new NotSupportedException($"oem[{oemId}]不支持平台rushmail");
        if (settings.UseIndependentRushmail)
        {
            return new RushMailClient(settings.RushmailCustomId, settings.RushmailSecretKey);
        }

        return new RushMailClient("4376", "8679e15771654cbf5dc28918b9d609b3");
    }
}

public class UniqueQueue<T> : IEnumerable<T>,
    System.Collections.ICollection,
    IReadOnlyCollection<T>
{
    private readonly Queue<T> _queue;
    private readonly HashSet<T> _hashSet;

    public UniqueQueue()
    {
        _queue = new Queue<T>();
        _hashSet = new HashSet<T>();
    }

    public void Enqueue(T item)
    {
        if (_hashSet.Add(item))
        {
            _queue.Enqueue(item);
        }
    }
    
    public T Dequeue()
    {
        var item = _queue.Dequeue();
        _hashSet.Remove(item);
        return item;
    }
    
    public bool TryDequeue([MaybeNullWhen(false)]out T result)
    {
        if (_queue.TryDequeue(out result))
        {
            _hashSet.Remove(result);
            return true;
        }

        return false;
    }

    public T Peek() => _queue.Peek();

    public IEnumerator<T> GetEnumerator() => _queue.GetEnumerator();

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public void CopyTo(Array array, int index)
    {
        ((ICollection) _queue).CopyTo(array, index);
    }

    int ICollection.Count => _queue.Count;
    int IReadOnlyCollection<T>.Count => _queue.Count;


    public bool IsSynchronized => true;
    public object SyncRoot => _queue;

}

public class BackgroundQueue : IHostedService
{
    private readonly IServiceProvider _sp;
    private readonly ILogger<BackgroundQueue> _logger;
    public static readonly UniqueQueue<EmailMarketing> Queue = new ();

    public BackgroundQueue(IServiceProvider sp, ILogger<BackgroundQueue> logger)
    {
        _sp = sp;
        _logger = logger;
    }
    public Task StartAsync(CancellationToken cancellationToken)
    {
        Task.Run(() => RunAsync(cancellationToken), cancellationToken);
        return Task.CompletedTask;
    }

    private async Task RunAsync(CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            if (Queue.TryDequeue(out var item))
            {
                _logger.LogInformation("开始执行营销任务#{Id}, 平台[{Platform}]", item.Id, item.EmailPlatform);
                switch (item.EmailPlatform)
                {
                    case EmailPlatform.Mailjet:
                        var service1 = _sp.CreateScope().ServiceProvider.GetRequiredService<MailjetApplicationService>();
                        await service1.RunAsync(item);
                        break;
                    case EmailPlatform.Rushmail:
                        var service2 = _sp.CreateScope().ServiceProvider.GetRequiredService<RushMailApplicationService>();
                        await service2.RunAsync(item);
                        break;
                    default:
                        _logger.LogError("未知平台{Platform}", item.EmailPlatform);
                        break;
                }
                _logger.LogInformation("剩余营销任务数量{Count}", ((ICollection)Queue).Count);
            }

            await Task.Delay(100, cancellationToken);
        }
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }
}